/**
 * 视频编码器 libX264
 * H264:编码时为了将数据进行压缩，这样在传输的过程中就不会使资源被浪费。H264采用了16*16的分块大小对，视频帧图像进
 *      行相似性比较和压缩编码
 *  1.H264相关概念
 *      1.序列：一段时间内图像变化不大的图像集我们可以称之为一个序列。序列也是理解为有相同特点的一段数据，但是如果某
 *          某个图像与之前的图像变换很大，很难参考之前的帧来生成新的帧，那么久结束一个序列，开始下一段序列。重复上
 *          一序列的做法，生成新的一段序列。
 *      2.帧类型：H264结构中，一张视频图像编码后的数据叫做一帧。一帧由一个或多个片(slice)组成，一个片由一个或多个
 *          宏块(MB)组成，一个宏块由16*16的yuv数据组成。宏块作为H264编码的基本单位
 *              H264使用帧内压缩和帧间压缩的方式提高编码压缩率
 *              H264采用了独特的I帧、P帧和B帧策略来实现，连续帧之间的压缩
 *          2.1.I帧:帧内编码帧，I帧表示关键帧，可以理解为这一帧画面的完整保留；解码时只需要本帧数据就可以完成
 *              I帧特点：
 *              1.它是一个全帧压缩编码帧，它将全帧图像信息进行JPEG压缩编码及传输
 *              2.解码时仅用I帧的数据就可重构完整图像
 *              3.I帧描述了图像北京和运动主体的详情
 *              4.I帧不需要参考其它画面而生成
 *              5.I帧是P帧和B帧的参考帧(其质量直接影响到同组中以后各帧的质量)
 *              6.I帧是帧组(GOP)的基础帧(第一帧)，在一组中只有一个I帧
 *              7.I帧不需要考虑运动矢量
 *              8.I帧所占数据的信息量比较大
 *          2.2.P帧:向前预测编码帧。P帧表示的是这一帧跟之前的一个关键(或P帧)的差别，解码时需要用到之前缓存的画面
 *              叠加上本帧定义的差别，生成最终画面。(也就是差别帧，P帧没有完整画面数据，只是与前一帧画面差别的数据)
 *              P帧特点:
 *              1.P帧是I帧后面相隔1~2帧的编码帧
 *              2.P帧采用运动补偿的方法传送它与前面的I或P帧的差值及运动矢量(预测误差)
 *              3.解码时必须将I帧中的预测值与预测误差求和后才能重构完整的P帧图像
 *              4.P帧属于前向预测的帧间编码，它只参考前面最靠近它的I帧或P帧
 *              5.P帧可以是其后面P帧的参考帧，也可以是其前后的B帧的参考帧
 *              6.由于P帧是参考帧，它可能造成解码错误的扩散
 *              7.由于是差值传送，P帧的压缩比较高
 *          2.3.B帧:双向预测内插编码帧。B帧是双向差别帧，也就是说B帧记录的是本帧与前后帧的差别。B帧以前面的I或P帧
 *              和后面的P帧为参考帧，找出"B帧"某点的预算值和两个运动矢量，并取预测差值和运动矢量传送。接受端根据运
 *              动矢量在两个参考帧中"算出"预测值并与差值求和，得到B帧"某点"样值，从而可得到完整的B帧
 *              B帧特点:
 *              1.B帧是由前面的I或P帧和后面的P帧来进行预测的
 *              2.B帧传送的是它与前面的的I或P帧和后面的P帧之间的预测误差及运动矢量
 *              3.B帧是双向预测编码帧
 *              4.B帧压缩比最高，因为它只反映两个参考帧运动主体的变化情况，预测比较准确
 *              5.B帧不是参考帧，不会造成解码错误的扩散
 *      3.GOP(画面组):Group of picture图像组，指两个I帧之间的距离(下面说的视频序列就是GOP)，Reference(参考周期)
 *          指两个P帧之间的距离。GOP值越大，P、B帧的数量会越多，平均每个I、P、B帧所占用的字节数就越多，也就更容易
 *          获取较好的图像质量；Reference越大，B帧的数量越多，同理也更容易获得较好的图像质量
 *          GOP结构一般有两个数字M、N，M指定I帧和P帧之间的距离，N指定两个I帧之间的距离。如果M=3，N=12，GOP的结构
 *          为:IBBPBBPBBPBBI。在一个GOP内I帧解码时不依赖任何的其它帧，P帧解码时则依赖前面的I帧或P帧，B帧解码依赖
 *          最近的一个I帧或P帧及其后最近的一个P帧
 *      4.IDR帧(关键帧):Instantaneous Refresh即时解码刷新，在编码解码中为了方便，将GOP中首个I帧要和其它I帧区别
 *          开，把第一个I帧叫IDR，这样方便控制编码和解码流程，所以IDR帧一定是I帧，但I帧不一定是IDR帧。IDR帧的作用
 *          是立即刷新，是错误不致传播，从IDR帧开始算新的序列开始编码。I帧有被跨帧参考的可能，IDR不会
 *  2.H264压缩方式
 *      H264采用的核心算法是帧内压缩和帧间压缩，帧内压缩是生成I帧的算法，帧间压缩是生成B帧和P帧的算法
 *      分组：将一系列变换不大的图像归为一个组，也就是一个序列，也可以叫GOP(画面组)
 *      定义帧：将每组的图像帧归分为I帧、P帧、B帧
 *      预测帧：以I帧做为基础帧，以I帧预测P帧，再由I帧和P帧预测B帧
 *      数据传输：最后将I帧数据与预测的差值信息进行存储和传输
 *  3.H264分层结构:H264原始码流是由一个接一个的NALU组成
 *      1.视频编码层(VCL:Video Coding Layer)
 *          VCL层是对核心算法引擎、块、宏块及片的语法级别的定义，负责有效表示视频数据的内容，最终输出编码完的
 *          数据SODB，在传输或存储之前，这些编码的VCL数据，被映射或封装进NAL单元
 *      2.网络抽象层(NAL:Network Coding Layer)
 *          NAL层定义了片级以上的语法级别(如序列参数集和图像参数集，针对网络传输)，负责以网络所要求的恰当方式去格
 *          式化数据并提供头信息，以保证数据适合各种信道和存储介质上的传输。NAL层将SODB打包成RBSP然后加上NAL头组
 *          成一个NALU单元
 *      一个NALU = 一组对应于视频编码的NALU头部信息 + 一个原始字节序列负荷(RBSP,Raw Byte Sepuence Payload)
 *      NALU结构单元的主体结构：一个原始的H.264 NALU单元通常由[StartCode][NALU Header][NALU Payload]三部分
 *      组成，其中StartCode用于标识这是一个NALU单元的开始，必须是"00 00 00 01"或"00 00 01"
 *      SODB(String Of Data Bits):数据比特串，是编码后的原始数据
 *      RBSP(Raw Byte Sequence Payload):原始字节序列负荷，即在SODB的后面添加了trailing bits。即一个bit 1和
 *      若干个bit 0，以便字节对齐。所以RBSP就等于SODB在它的最后一个字节的最后一个比特后，紧跟值为1的1个比特，然后
 *      增加若干比特的0，已补齐这个字节
 *  4.H264码流结构
 *      |NAL|NAL|NAL| NAL |
 *      |SPS|PPS|IDR|SLICE|SLICE|SLICE|...
 *      SLICE:HEADER + DATA
 *      DATA:MACROBLOCK + MACROBLOCK + MACROBLOCK + ....
 *  5.H264的NAL单元:在实际的网络数据传输过程中H264的数据结构是以NALU(NAL单元)进行传输的，传输数据结构组成为如下
 *      |nal header|RBSP|nal header|RBSP|nal header|RBSP|...
 *      1.NAL头()
 *          组成:forbidden_bit + nal_reference_bit + nal_unit_type
 *          nal_header() {
 *              forbidden_bit;                                                  1
 *              nal_reference_bit;                                              2
 *              nal_unit_type;                                                  5
 *          }
 *              字段解析:
 *              forbidden_bit:占用1bit
 *                  禁止位，当禁止位值为1时表示语法错误
 *              nal_reference_bit:占用2bit
 *                  NAL重要性，值越大，越重要。解码器在解码处理不过来的时候，可以丢掉重要性为0的NALU
 *              nal_unit_type:占用5bit，
 *                  表示NAL单元的数据类型
 *                      nal_unit_type   NAL单元和RBSP语法结构的内容
 *                      0               未规定
 *                      1               非IDR图像的编码条带
 *                                      slice_layer_without_partitioning_rbsp()
 *                      2               非IDR图像的编码条带数据分割块A
 *                                      slice_data_partition_a_layer_rbsp()
 *                      3               非IDR图像的编码条带数据分割块B
 *                                      slice_data_partition_b_layer_rbsp()
 *                      4               非IDR图像的编码条带数据分割块C
 *                                      slice_data_partition_c_layer_rbsp()
 *                      5               IDR图像的编码条带
 *                                      slice_layer_without_partitioning_rbsp()
 *                      6               辅助增强信息(SEI)
 *                                      sei_rbsp()
 *                      7               序列参数集(SPS)
 *                                      seq_parameter_set_rbsp()
 *                      8               图像参数集(PPS)
 *                                      pic_parameter_set_rbsp()
 *                      9               访问单元分隔符(Access Unit Delimiter)
 *                                      access_unit_delimiter_rbsp()
 *                      10              序列结尾(End Of Sequence)
 *                                      end_of_seq_rbsp()
 *                      11              流结尾(End Of Stream)
 *                                      end_of_stream_rbsp()
 *                      12              填充数据(Filler)
 *                                      filler_data_rbsp()
 *                      13              序列参数集扩展
 *                                      seq_parameter_set_extension_rbsp()
 *                      14 .. 18        保留
 *                      19              未分割的辅助编码图像的编码条带
 *                                      slice_layer_without_partitioning_rbsp()
 *                      20 .. 23        保留
 *                      24              未指定
 *              nal_unit_typew为1、2、3、4、5及12的NAL单元称为VCL的NAL单元，其他类型的NAL单元为非VCL的
 *                  NAL单元
 *          非VCL的NAL数据类型:
 *              SPS(Sequence Parameter Set:序列参数集):包含一些通用的参数，比如profile和level，视频帧的尺
 *                  寸，参考帧的最大数量等
 *              PPS(Picture Parameter Set:图像参数集):包含一些通用的参数，比如熵编码类型，有效的参考图像的数
 *                  目和初始化参数等
 *              SEI(补充增强信息):这部分参数可作为H264的比特流数据而被传输，每一个SEI信息被封装成一个NAL单元，
 *                  SEI对于解码器来说可能是有用的，但是对于基本的解码过程来说，并不是必须得
 *          VCL的NAL数据类型:
 *              头信息块:包括宏块类型，量化参数，运动矢量。
 *              帧内编码信息数据块:称为B类数据分块，它包含帧内编码宏块类型，帧内编码系数
 *              帧间编码信息数据块:称为C类数据分块，它包含帧间编码宏块类型，帧间编码系数。
 *
 *
 */

#include "X264Encoder.h"

const AVCodec *X264Encoder::findCodec(EncoderParam &encoderParam) {
  return avcodec_find_encoder_by_name("libx264");
}

int32_t X264Encoder::initContextParam(AVCodecContext *pCodecCtx, EncoderParam &encoderParam) {
  pCodecCtx->width = encoderParam.width;
  pCodecCtx->height = encoderParam.height;
  pCodecCtx->bit_rate = encoderParam.bitRate;
  pCodecCtx->pix_fmt = AV_PIX_FMT_YUV420P;
  //编码器上下文的时间基
  pCodecCtx->time_base = av_make_q(1, encoderParam.frameRate);
  //编码器上下文的帧率
  pCodecCtx->framerate = av_make_q(encoderParam.frameRate, 1);

  pCodecCtx->gop_size = 12;
  pCodecCtx->max_b_frames = 1;

  //针对x264的设置
  pCodecCtx->profile = FF_PROFILE_H264_MAIN;
  av_opt_set(pCodecCtx->priv_data, "preset", "slow", 0);

  return SUCCESS;
}

MediaType X264Encoder::getMediaType() {
  return MediaType_Video;
}

const char *X264Encoder::getClassName() {
  return "X264Encoder";
}

void X264Encoder::toString() {
  LOG_TEST("X264Encoder()")
}
